﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using FinosLibrary;
using System.IO;
using System.Threading;
using System.Net;
namespace FinosServiceLibrary.Server {
    public class Client : FMarshalByRefObject {
        public string byteHead;
        public AppLoader appBelong { get; set; }
        public static Dictionary<int, bool> IDS = new Dictionary<int, bool>();
        public NetworkStream stream;
        public EndPoint remoteEndPoint;
        public int ID;
        public string id { get { return "c"+ID.ToString(); } }
        public static Dictionary<string, Client> clientMap = new Dictionary<string, Client>();
        public static Client getClientByID(string id) {
            return clientMap[id];
        }
        public static int getID() {
            int id = -1;
            try {
                id = IDS.First(x => x.Value).Key;
            } catch (Exception) {
                id = IDS.Count;
            }
            IDS[id] = false;
            return id;
        }
        public Client() {
            ID = getID();
            clientMap[id] = this;
        }
        public void WaitForSendData(Object stateInfo) {
            int numberOfBytesRead = -1;
            int headInfo = 0;
            int waitSize = 4;
            byte[] headBytes = new byte[4];
            using (MemoryStream buffer = new MemoryStream()) {
                while (numberOfBytesRead != 0) {
                    if (headInfo == 0) {
                        if (waitSize == 4) headBytes = new byte[4];
                        try {
                            if (stream.CanRead) {
                                numberOfBytesRead = stream.Read(headBytes, 4 - waitSize, waitSize);
                                if (numberOfBytesRead == 0) break;
                            } else {
                                continue;
                            }
                        } catch (IOException) {
                            break;
                        }
                        if (numberOfBytesRead < waitSize) {
                            waitSize = waitSize - numberOfBytesRead;
                        } else {
                            headInfo = BitConverter.ToInt32(headBytes, 0);
                            waitSize = headInfo;
                            buffer.Seek(0, SeekOrigin.Begin);
                            buffer.SetLength(headInfo);
                        }
                    } else {
                        byte[] bytes = new byte[1024];
                        try {
                            if (stream.CanRead) {
                                numberOfBytesRead = stream.Read(bytes, 0, waitSize >= 1024 ? 1024 : waitSize);
                                if (numberOfBytesRead == 0) break;
                            } else {
                                continue;
                            }
                        } catch (IOException) {
                            break;
                        }
                        buffer.Write(bytes, 0, numberOfBytesRead);
                        waitSize -= numberOfBytesRead;
                        if (waitSize == 0) {
                            buffer.Seek(0, SeekOrigin.Begin);
                            bytes = new byte[buffer.Length];
                            buffer.Read(bytes, 0, bytes.Length);
                            onData(bytes);
                            headInfo = 0;
                            waitSize = 4;
                        }
                    }
                }
            }
            Logger.trace("Close Connection:" + remoteEndPoint.ToString());
            onDisconnect();
        }
        public void onDisconnect() {
            if (appBelong != null)
                appBelong.onDisconnect(id);
            stream.Close();
            IDS[ID] = true;
            clientMap.Remove(id);
            AdminClient.admins.Remove(id);
        }
        protected void onData(byte[] bytes) {
            Package package;
            if (byteHead != null) {
                package = new Package(byteHead) { byteBody = bytes };
            } else {
                package = bytes.FromJsonTo<Package>();
                if (package.head == "connect") {
                    if (!AppManager.ConnectApp(this, package.body)) {
                        onDisconnect();
                    } else {
                        call("NetConnection_Connected");
                    }
                    return;
                }
            }
            if (this is AdminClient) {
                appBelong.doLogic(package, this);
            } else {
                appBelong.doLogic(bytes, id);
            }
        }
        public void call(string fun, object body) {
            call(new Package(fun, body).toBytes());
        }
        public void call(string fun) {
            call(new Package(fun).toBytes());
        }
        public void call(Package p) {
            call(p.toBytes());
        }
        public void call(byte[] bytes) {
            if(stream.CanWrite){
                try {
                    stream.Write(bytes, 0, bytes.Length);
                } catch (Exception) {
                }
            }
        }
    }
}